package ru.papergames.battleserver.model.battle.npc.processors;

import org.apache.log4j.Logger;
import ru.common.util.Point3D;
import ru.papergames.battleserver.model.PaperGameNpcUser;
import ru.papergames.battleserver.model.battle.AbstractBattle;
import ru.papergames.battleserver.model.battle.BattlePlayer;
import ru.papergames.battleserver.model.battle.BattlePlayerAction;
import ru.papergames.battleserver.model.battle.npc.*;
import ru.papergames.battleserver.util.pathfinding.AStarPathFinder;
import ru.papergames.battleserver.util.pathfinding.SimplePathFinder;
import ru.papergames.battleserver.util.pathfinding.heuristic.ManhattanHeuristic;

import java.util.*;

/**
 * Date: 11.05.12 22:22
 *
 * @author Alexander Troshanin
 */
public class SimpleNpcActionProcessor extends AbstractNpcActionProcessor {

    private static final Logger log = Logger.getLogger(SimpleNpcActionProcessor.class.getName());

    private static SimpleNpcActionProcessor instance = new SimpleNpcActionProcessor();

    protected SimpleNpcActionProcessor() {
    }

    public static SimpleNpcActionProcessor getInstance() {
        return instance;
    }

    @Override
    protected void processPlayer(AbstractBattle battle, BattlePlayer npcPlayer) {
        if (System.currentTimeMillis() - npcPlayer.getLastActionFinishTime() < (2 + rnd.nextInt(5)) * 1000) {
            return;
        }

        PaperGameNpcUser user = (PaperGameNpcUser) npcPlayer.getGameUser();
        NpcDataContainer dataContainer = user.getDataContainer();
        if (dataContainer.getPacketsCountToBeProcessed() > 0) {
            return;
        }
        if (dataContainer.getUnlockedNpcLines().size() > 0) {
            log.debug("Trying to move some ball to one unlocked line");
            List<NpcLine> lines = new ArrayList<NpcLine>(dataContainer.getUnlockedNpcLines());
            Collections.sort(lines, new Comparator<NpcLine>() {
                @Override
                public int compare(NpcLine line1, NpcLine line2) {
                    return line2.getBalls().size() - line1.getBalls().size();
                }
            });
            for (NpcLine line : lines) {
                if (rnd.nextBoolean()) {
                    if (tryToFindAndMoveBallToCell(battle, npcPlayer, dataContainer, line, line.getLeftBorderCell())) {
                        return;
                    }
                    if (tryToFindAndMoveBallToCell(battle, npcPlayer, dataContainer, line, line.getRightBorderCell())) {
                        return;
                    }
                } else {
                    if (tryToFindAndMoveBallToCell(battle, npcPlayer, dataContainer, line, line.getRightBorderCell())) {
                        return;
                    }
                    if (tryToFindAndMoveBallToCell(battle, npcPlayer, dataContainer, line, line.getLeftBorderCell())) {
                        return;
                    }
                }
            }
        }
        if (dataContainer.getUnlockedNpcBalls().size() > 1) {
            log.info("No unlocked Trying");
            List<NpcBall> unlockedBalls = new ArrayList<NpcBall>(dataContainer.getUnlockedNpcBalls());
            Collections.shuffle(unlockedBalls);

            for (NpcBall b1 : unlockedBalls) {
                for (NpcBall b2 : unlockedBalls) {
                    if (b1 != b2 && !belongToSameLine(b1, b2)) {

                        if (tryToMoveBallToCell(battle, npcPlayer, dataContainer, b2, findBestNeighbourCellToMoveTo(dataContainer.getMap(), b1))) {
                            return;
                        }

                    }
                }
            }
        }


        log.info("Did not found any balls to move");
        battle.executeSynchronizedAction(npcPlayer, BattlePlayerAction.FORCE_TURN_FINISH, new Object[] {});

        /*
        if (System.currentTimeMillis() - npcPlayer.getLastActionFinishTime() > 3 * 1000) {
            battle.executeSynchronizedAction(npcPlayer, BattlePlayerAction.FORCE_TURN_FINISH, new Object[] {});
        }
        */
    }

    private NpcCell findBestNeighbourCellToMoveTo(NpcBattleMap map, NpcBall b1) {
        NpcCell cell = b1.getCell();
        NpcCell result = null;
        for (LineVector vector : LineVector.values()) {
            NpcCell c1 = map.getCell(cell.getX() + vector.getDx(), cell.getY() + vector.getDy());
            NpcCell c2 = map.getCell(cell.getX() - vector.getDx(), cell.getY() - vector.getDy());

            int t = 0;
            if (c1 != null && c1.isCellFree()) {
                result = c1;
                t++;
            }
            if (c2 != null && c2.isCellFree()) {
                result = c2;
                t++;
            }
            if (t == 2) {
                return rnd.nextBoolean() ? c1 : c2;
            }
        }
        return result;
    }

    private boolean belongToSameLine(NpcBall b1, NpcBall b2) {
        for (LineVector vector : LineVector.values()) {
            NpcLine l1 = b1.getLine(vector);
            NpcLine l2 = b2.getLine(vector);
            if (l1 != null && l2 != null && l1 == l2) {
                return true;
            }

        }
        return false;  //To change body of created methods use File | Settings | File Templates.
    }

    private boolean tryToFindAndMoveBallToCell(AbstractBattle battle, BattlePlayer npcPlayer, NpcDataContainer dataContainer, NpcLine line, final NpcCell cell) {

        if (rnd.nextBoolean()) {
            log.debug("Trying to move ball from blocked line");
            for (NpcLine blockedLine : dataContainer.getBlockedNpcLines()) {

                for (NpcBall ball : blockedLine.getBalls()) {
                    if (!ball.isBlocked()) {
                        // ok we find ball that probably could be moved to cell
                        if (tryToMoveBallToCell(battle, npcPlayer, dataContainer, ball, cell)) {
                            return true;
                        }
                    }
                }

            }

        }

        TreeSet<NpcBall> sortedBalls = new TreeSet<NpcBall>(new Comparator<NpcBall>() {
            @Override
            public int compare(NpcBall b1, NpcBall b2) {
                return distance(b1.getCell(), cell) - distance(b2.getCell(), cell);
            }
        });
        sortedBalls.addAll(dataContainer.getUnlockedNpcBalls());

        for (NpcBall ball : sortedBalls) {
            if (ball.getLine(line.getVector()) != line) {
                // ok we find ball that probably could be moved to cell
                if (tryToMoveBallToCell(battle, npcPlayer, dataContainer, ball, cell)) {
                    return true;
                }
            }
        }

        return false;
    }

    private boolean tryToMoveBallToCell(AbstractBattle battle, BattlePlayer npcPlayer, NpcDataContainer dataContainer, NpcBall ball, NpcCell cell) {
        log.debug("Trying to move ball to cell");
        SimplePathFinder simplePathFinder = new SimplePathFinder(dataContainer.getMap());
        int sx = ball.getCell().getX();
        int sy = ball.getCell().getY();
        int tx = cell.getX();
        int ty = cell.getY();
        List<Point3D> route = simplePathFinder.findPath(sx, sy, tx, ty);

        if (route == null) {
            NpcBattleMap map = dataContainer.getMap();
            AStarPathFinder aStarPathFinder = new AStarPathFinder(map, 3 * (map.getWidth() + map.getHeight()), true, new ManhattanHeuristic(10), 3000);
            route = aStarPathFinder.findPath(sx, sy, tx, ty);
            if (route == null) {
                return false;
            }
            Collections.reverse(route);
        }

        battle.executeSynchronizedAction(npcPlayer, BattlePlayerAction.MOVE_BALL, new Object[] {route});
        return true;
    }
}
